﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;

namespace Meechisoft.TFS.BranchIndicator
{
    using Microsoft.TeamFoundation.Client;

    using EnvDTE;
    using EnvDTE80;
    using Microsoft.TeamFoundation.Framework.Client;
    using System.Windows.Media;

    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    [Guid(GuidList.BranchIndicatorPackage)]
    [ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids80.NoSolution)]
    public sealed class BranchIndicatorPackage : Package, IVsShellPropertyEvents, IVsSolutionEvents
    {

        private static BranchIndicatorPackage _Instance;

        private DTE dte = null;
        private DTE2 dte2 = null;
        private ITeamFoundationContextManager tfscm;
        private ITeamFoundationContext tfscontext;
        private VSWindow window;
        private IVsShell shell;
        private IVsSolution solution;
        private uint shellcookie;
        private uint solutioncookie;
        protected override void Initialize()
        {
            try
            {
                base.Initialize();

                this.dte = (DTE)this.GetService(typeof(EnvDTE.DTE));
                this.dte2 = (DTE2)dte;

                AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

                AttachShellEvents();
                AttachSolutionEvents();
                AttachToTFS();
                AttachWindow();

            }
            catch (Exception)
            {
                throw;
            }

            //NOTE: Not sure how to get an the instance to my package with the explorer components
            _Instance = this;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;

                DetachTFSEvents();
                DetachWindow();
                DetachShellEvents();
                DetachSolutionEvents();
            }

            base.Dispose(disposing);
        }


        private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            var uri = new Uri(this.GetType().Assembly.CodeBase);
            if (!uri.IsFile)
                return null;

            var file = new System.IO.FileInfo(uri.LocalPath);

            var an = new System.Reflection.AssemblyName(args.Name);

            var assemblyFile = System.IO.Path.Combine(file.Directory.FullName, an.Name + ".dll");

            if (System.IO.File.Exists(assemblyFile))
                return System.Reflection.Assembly.LoadFrom(assemblyFile);

            return null;
        }

        public static BranchIndicatorPackage Instance
        {
            get
            {
                return _Instance;
            }
        }

        #region Attach & Detach

        private void AttachWindow()
        {
            try
            {
                this.window = new VSWindow(dte);
            }
            catch (NullReferenceException) { }
        }

        private void DetachWindow()
        {
            if (window != null)
                this.window.Reset();
        }


        private void AttachShellEvents()
        {
            DetachShellEvents();
            this.shell = this.GetService(typeof(SVsShell)) as IVsShell;
            shellcookie = 0;
            if (this.shell != null)
                this.shell.AdviseShellPropertyChanges(this, out shellcookie);
        }

        private void DetachShellEvents()
        {
            if (shell != null)
            {
                if (shellcookie != uint.MaxValue)
                {
                    shell.UnadviseShellPropertyChanges(shellcookie);
                    shellcookie = uint.MaxValue;
                }

                shell = null;
            }
        }

        private void AttachSolutionEvents()
        {
            DetachSolutionEvents();

            this.solution = this.GetService(typeof(SVsSolution)) as IVsSolution;
            solutioncookie = 0;
            if (solution != null)
                solution.AdviseSolutionEvents(this, out solutioncookie);
        }


        private void DetachSolutionEvents()
        {
            if (solution != null)
            {
                if (solutioncookie != uint.MaxValue)
                {
                    solution.UnadviseSolutionEvents(solutioncookie);
                    solutioncookie = uint.MaxValue;
                }

                solution = null;
            }
        }


        private void AttachToTFS()
        {
            DetachTFSEvents();

            this.tfscm = (ITeamFoundationContextManager)this.GetService(typeof(ITeamFoundationContextManager));

            if (tfscm != null)
                tfscm.ContextChanged += tfscm_ContextChanged;
        }

        private void DetachTFSEvents()
        {
            if (tfscm == null)
                return;
            tfscm.ContextChanged -= tfscm_ContextChanged;
        }

        #endregion

        #region Shell Events

        int IVsShellPropertyEvents.OnShellPropertyChange(int propid, object var)
        {
            if (((int)__VSSPROPID.VSSPROPID_Zombie) == propid)
            {
                var val = Convert.ToBoolean(var);

                if (!val)
                {
                    AttachWindow();
                    AttachToTFS();
                }
            }

            return VSConstants.S_OK;
        }

        #endregion

        #region Solution Events

        public int OnAfterCloseSolution(object pUnkReserved)
        {
            if (window != null)
                window.Reset();
            return VSConstants.S_OK;
        }

        public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy)
        {
            return VSConstants.S_OK;
        }

        public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
        {
            return VSConstants.S_OK;
        }

        public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
        {
            try
            {
                CheckForSolutionInBranch();
            }
            catch (Exception ex)
            { }
            return VSConstants.S_OK;
        }

        public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved)
        {
            return VSConstants.S_OK;
        }

        public int OnBeforeCloseSolution(object pUnkReserved)
        {
            return VSConstants.S_OK;
        }

        public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy)
        {
            return VSConstants.S_OK;
        }

        public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel)
        {
            return VSConstants.S_OK;
        }

        public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel)
        {
            return VSConstants.S_OK;
        }

        public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel)
        {
            return VSConstants.S_OK;
        }

        #endregion

        private void tfscm_ContextChanged(object sender, ContextChangedEventArgs e)
        {
            this.tfscontext = e.NewContext;

            CheckForSolutionInBranch();
        }

        public void CheckForSolutionInBranch()
        {
            if (window == null)
                return;

            var branch = TfsHelpers.GetBranchFromSolution(this, dte.Solution, tfscontext);

            if (branch == null)
            {
                window.Reset();
                return;
            }

            var registry = tfscontext.TeamProjectCollection.ConfigurationServer.GetService<ITeamFoundationRegistry>();

            var setting = new BranchSetting() { ID = branch.Properties.RootItem.Item.GetHashCode().ToString() };
            
            setting.Name = branch.Properties.RootItem.Item;

            registry.PopulateBranchColours(setting);

            if (setting.PrimaryColor != Colors.Transparent)
                window.ChangeTitleBar(setting.PrimaryColor, setting.Gradient);
            else
                window.ResetTitleBar();

            if (setting.SecondaryColor != Colors.Transparent)
                window.ChangeTitleFont(setting.SecondaryColor);
            else
                window.ResetTitleFont();

        }



    }
}
